#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Modbus TCP模拟器 - 重构版
参考 utils/modbus/modbus_tcp.py 实现
确保数据更新和采集的一致性
"""

import random
import time
import threading
import subprocess
from typing import Optional, Callable
from datetime import datetime

from pymodbus.server import StartTcpServer, ServerStop
from pymodbus.datastore import ModbusServerContext, ModbusDeviceContext
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus import ModbusDeviceIdentification


class ModbusTcpSimulator:
    """Modbus TCP设备模拟器 - 基于modbus_tcp.py的实现思路"""

    def __init__(self, message_callback: Optional[Callable] = None):
        """初始化TCP模拟器"""
        self.server_ref = None
        self.start_slave_id = None
        self.callback = message_callback or self._default_callback
        self.running = False

        # 配置参数
        self.host = None
        self.port = None
        self.slave_count = 0
        self.start_address = 0
        self.register_count = 0
        self.update_interval = 2

        # 线程
        self.update_thread = None
        self.server_thread = None

        # 全局上下文（参考modbus_tcp.py，使用全局确保一致性）
        self.slave_contexts = {}  # 全局从站上下文
        self.context = None       # 全局服务器上下文
        self._identity = None      # 设备标识

        self.tcp_server = None

    def _default_callback(self, message: str, level: str = "INFO", msg_type: str = "normal"):
        """默认回调"""
        print(f"[{level}] {message}")

    def _notify(self, message: str, level: str = "INFO", msg_type: str = "normal"):
        """发送通知"""
        self.callback(message, level, msg_type)

    def _create_slave_context(self, register_count: int):
        """创建单个从站的数据存储区（参考modbus_tcp.py）"""
        return ModbusDeviceContext(
            di=ModbusSequentialDataBlock(0, [0] * 10),
            co=ModbusSequentialDataBlock(0, [0] * 10),
            hr=ModbusSequentialDataBlock(0, [0] * register_count),
            ir=ModbusSequentialDataBlock(0, [0] * 10),
        )

    def _create_device_identity(self):
        """创建设备标识（参考modbus_tcp.py）"""
        identity = ModbusDeviceIdentification()
        identity.VendorName = 'IoT-Util'
        identity.ProductCode = 'ModbusTCPSim'
        identity.ProductName = 'Modbus TCP Simulator'
        identity.ModelName = 'TCP Model'
        identity.MajorMinorRevision = '1.0'
        return identity

    def configure(self, host: str, port: int, slave_count: int, start_slave_id: int, start_address: int,
                  register_count: int, update_interval: int) -> bool:
        """配置TCP模拟器"""
        try:
            self.host = host
            self.port = port
            self.slave_count = slave_count
            self.start_slave_id = start_slave_id
            self.start_address = start_address
            self.register_count = register_count
            self.update_interval = update_interval

            # 创建全局从站上下文（参考modbus_tcp.py的全局变量做法）
            self.slave_contexts = {
                slave_id: self._create_slave_context(register_count)
                for slave_id in range(self.start_slave_id, self.start_slave_id + slave_count)
            }

            # 创建全局服务器上下文（多从站模式）
            self.context = ModbusServerContext(devices=self.slave_contexts, single=False)

            # 创建设备标识
            self._identity = self._create_device_identity()

            self._notify(f"✅ TCP模拟器配置成功: {host}:{port}, {slave_count}个从站", "SUCCESS", "normal")
            return True

        except Exception as e:
            self._notify(f"❌ TCP模拟器配置失败: {e}", "ERROR", "error")
            return False

    def start(self) -> bool:
        """启动TCP模拟器"""
        if self.running:
            self._notify("⚠️ TCP模拟器已在运行中", "WARNING", "normal")
            return True

        if not self.context:
            self._notify("❌ 请先配置TCP模拟器", "ERROR", "error")
            return False

        try:
            self.running = True

            # 创建服务器引用用于控制停止（参考modbus_tcp.py）
            self.server_ref = type('ServerRef', (), {'running': True})()

            # 启动数据更新线程（参考modbus_tcp.py的update_registers函数）
            self.update_thread = threading.Thread(
                target=self._update_registers_loop,
                daemon=True
            )
            self.update_thread.start()

            # 启动TCP服务器线程
            self.server_thread = threading.Thread(
                target=self._run_server,
                daemon=True
            )
            self.server_thread.start()

            self._notify(f"🚀 TCP模拟器启动成功: {self.host}:{self.port}", "SUCCESS", "normal")
            return True

        except Exception as e:
            self.running = False
            self._notify(f"❌ TCP模拟器启动失败: {e}", "ERROR", "error")
            return False

    def _run_server(self):
        """运行TCP服务器（参考modbus_tcp.py）"""
        try:
            self._notify(f"🌐 TCP服务器在 {self.host}:{self.port} 启动", "INFO", "normal")

            # 启动服务器（阻塞运行）
            StartTcpServer(
                context=self.context,
                identity=self._identity,
                address=(self.host, self.port)
            )

        except Exception as e:
            self._notify(f"❌ TCP服务器运行错误: {e}", "ERROR", "error")
        finally:
            self.running = False

    def _update_registers_loop(self):
        """更新寄存器数据循环（参考modbus_tcp.py的update_registers函数）"""
        self._notify("🔄 开始更新TCP模拟器寄存器...", "INFO", "normal")

        while getattr(self.server_ref, 'running', True):
            try:
                # 直接使用全局的slave_contexts（参考modbus_tcp.py第86行）
                for slave_id, slave_context in self.slave_contexts.items():
                    # 生成随机数值
                    values = [random.randint(0, 65535) for _ in range(self.register_count - 1)]
                    slave_context.setValues(3, self.start_address, values)
                    self._notify(
                        f"📊 TCP从站{slave_id}: HR[0-5]={values}",
                        "INFO",
                        "normal"
                    )
                time.sleep(self.update_interval)
            except Exception as e:
                self._notify(f"❌ TCP寄存器更新错误: {e}", "ERROR", "error")
                time.sleep(self.update_interval)

    def stop(self) -> bool:
        """停止TCP模拟器"""
        try:
            self.running = False

            # 停止服务器引用
            if hasattr(self, 'server_ref'):
                self.server_ref.running = False

            self._notify(f"🔌 TCP模拟器 {self.host}:{self.port} 正在停止...", "INFO", "normal")

            # 等待线程结束
            time.sleep(0.5)

            # 强制关闭端口（查找并杀掉占用端口的进程）
            ServerStop()

            self._notify(f"⏹️ TCP模拟器已停止", "INFO", "normal")
            return True

        except Exception as e:
            self._notify(f"❌ TCP模拟器停止失败: {e}", "ERROR", "error")
            return False

    def _kill_port_process(self, port: int):
        """查找并杀掉占用指定端口的进程"""
        try:
            # 查找占用端口的进程
            result = subprocess.run(
                ['lsof', '-ti', f':{port}'],
                capture_output=True,
                text=True,
                timeout=2
            )

            if result.returncode == 0 and result.stdout.strip():
                pids = result.stdout.strip().split('\n')
                for pid in pids:
                    try:
                        # 杀掉进程
                        subprocess.run(['kill', '-9', pid], timeout=1)
                        self._notify(f"✅ 已杀掉进程 PID: {pid}", "INFO", "normal")
                    except Exception:
                        pass
            else:
                self._notify(f"ℹ️ 未找到占用端口 {port} 的进程", "INFO", "normal")

        except Exception as e:
            # 静默失败，不影响主流程
            pass

    def is_running(self) -> bool:
        """检查是否运行中"""
        return self.running
